#include <Time.h>
#include <OneWire.h>
#include <DallasTemperature.h>

const int PIR_SENSOR_1_PIN = 2;
const int PIR_SENSOR_2_PIN = 3;
const int LED_1 = 9;
const int LED_2 = 8;
const int LED_DOOR = 12;
const int LED_PRESSURE_REAR = 13;
const int LED_PRESSURE_FRONT = 11;
const int DOOR_BUTTON = 7;
const int FRONT_PRESSURE_SWITCH = 6;
const int REAR_PRESSURE_SWITCH = 5;
const int ONE_WIRE_BUS = 10;
const int speakerOut = 4;
const int TIMEOUT = 60;

const float MIN_TEMP = 50.0f;
const float MAX_TEMP = 85.0f;

time_t initialDangerTime;
time_t initialWaitTime;
bool enteredBabyInCarState;
bool enteredWaitState;

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
// DS18S20 Temperature chip i/o
OneWire ds(ONE_WIRE_BUS);

enum
{
  CAR_UNOCCUPIED,
  DRIVER_IN_SEAT,
  DOOR_OPEN_NO_DRIVER,
  DOOR_OPEN_WITH_DRIVER,
  BABY_ALONE_IN_CAR,
  WAIT_FOR_DRIVER,
  ALARM,
  TEXT_ALERT
};

int currentState;

void setup()
{
  Serial.begin(9600);
  currentState = CAR_UNOCCUPIED;
  Serial.write("\nState: CAR_UNOCCUPIED\n");
  
  pinMode(PIR_SENSOR_1_PIN, INPUT);
  pinMode(PIR_SENSOR_2_PIN, INPUT);
  pinMode(LED_1, OUTPUT);
  pinMode(LED_2, OUTPUT);
  pinMode(LED_DOOR, OUTPUT);
  pinMode(LED_PRESSURE_REAR, OUTPUT);
  pinMode(LED_PRESSURE_FRONT, OUTPUT);
  pinMode(DOOR_BUTTON, INPUT);
  pinMode(FRONT_PRESSURE_SWITCH, INPUT);
  pinMode(REAR_PRESSURE_SWITCH, INPUT);
  pinMode(speakerOut,OUTPUT);
  
  digitalWrite(LED_1, LOW);
  digitalWrite(LED_2, LOW);
  digitalWrite(LED_DOOR, LOW);
  digitalWrite(LED_PRESSURE_REAR, LOW);
  digitalWrite(LED_PRESSURE_FRONT, LOW);
  digitalWrite(speakerOut, LOW);
  
  enteredBabyInCarState = false;
  enteredWaitState = false;
  
  delay(3000);
}

void loop()
{
  //delay(500);
  
  bool rearMotion = checkRearMotion();
  bool frontMotion = checkFrontMotion();
  bool doorClosed = checkDoorClosed();
  bool rearPressure = checkRearPressure();
  bool frontPressure = checkFrontPressure();
  float temp = readTemperature();
  Serial.println(temp);
  
  switch(currentState)
  {
  case CAR_UNOCCUPIED:    
    //on door open, goto DOOR_OPEN_NO_DRIVER
    if (!doorClosed)
    {
      currentState = DOOR_OPEN_NO_DRIVER;
      Serial.write("\nState: DOOR_OPEN_NO_DRIVER\n");
    }
      
    //on motion or pressure, goto BABY_ALONE_IN_CAR
    else if (rearMotion || rearPressure || frontPressure || frontMotion)
    {
      currentState = BABY_ALONE_IN_CAR;
      Serial.write("\nState: BABY_ALONE_IN_CAR\n");
    }
      
    break;    
    
  case DRIVER_IN_SEAT:    
    //on door open, goto DOOR_OPEN_WITH_DRIVER
    if (!doorClosed)
    {
      currentState = DOOR_OPEN_WITH_DRIVER;
      Serial.write("\nState: DOOR_OPEN_WITH_DRIVER\n");
    }
      
    break;
    
  case DOOR_OPEN_NO_DRIVER:    
    //on door close, goto CAR_UNOCCUPIED
    if (doorClosed)
    {
      currentState = CAR_UNOCCUPIED;
      Serial.write("\nState: CAR_UNOCCUPIED\n");
    }
    
    //on %increased pressure, goto DOOR_OPEN_WITH_DRIVER [questionable!]
    else if (frontPressure)
    {
      currentState = DOOR_OPEN_WITH_DRIVER;
      Serial.write("\nState: DOOR_OPEN_WITH_DRIVER\n");
    }
    
    break;
    
  case DOOR_OPEN_WITH_DRIVER:    
    //on door close, goto DRIVER_IN_SEAT
    if (doorClosed)
    {
      currentState = DRIVER_IN_SEAT;
      Serial.write("\nState: DRIVER_IN_SEAT\n");
    }
      
    //on threshold % reduced pressure, goto DOOR_OPEN_NO_DRIVER
    else if (!frontPressure)
    {
      currentState = DOOR_OPEN_NO_DRIVER;
      Serial.write("\nState: DOOR_OPEN_NO_DRIVER\n");
    }
    
    break;
    
  case BABY_ALONE_IN_CAR:
    if (!enteredBabyInCarState)
    {
      enteredBabyInCarState = true;
      initialDangerTime = now();
    }
    
    //on temp out of range, goto ALARM
    else if (temp > MAX_TEMP || temp < MIN_TEMP)
    {
      currentState = ALARM;
      Serial.write("\nState: ALARM\n");
    }
    
    //on 10 mins. elapsed, goto TEXT_ALERT (replaced here by ALARM)
    else if (now() - initialDangerTime > TIMEOUT)
    {
      currentState = ALARM;
      Serial.write("\nState: TIMEOUT ALARM\n");
    }
      
    //on door open, goto WAIT_FOR_DRIVER
    else if (!doorClosed)
    {
      currentState = WAIT_FOR_DRIVER;
      Serial.write("\nState: WAIT_FOR_DRIVER\n");
    }
      
    break;
    
  case WAIT_FOR_DRIVER:    
    if (!enteredWaitState)
    {
      initialWaitTime = now();
      enteredWaitState = true;
    }
    
    //on %increased pressure, goto DOOR_OPEN_WITH_DRIVER
    else if (frontPressure)
    {
      currentState = DOOR_OPEN_WITH_DRIVER;
      Serial.write("\nState: DOOR_OPEN_WITH_DRIVER\n");
    }
    
    //on 1 min elapsed or door close, goto BABY_ALONE_IN_CAR
    else if (now() - initialWaitTime > 60)
    {
      currentState = BABY_ALONE_IN_CAR;
      Serial.write("\nState: BABY_ALONE_IN_CAR\n");
    }
    
    break;
    
  case ALARM:
    soundSpeaker();
    break;
    
  case TEXT_ALERT:
    break;  
    
  default:
    Serial.write("\nError: some kind of crazy unknown state.n");
    break;    
  }  
}

void soundSpeaker()
{
   //int sensorValue = analogRead(0);
   //Serial.println(sensorValue, DEC);
   //digitalWrite(ledPin,HIGH);  

   int burst = 0;
   int length = 0;
   
   int Tone = 200;
   
       // create a buzz noise
    while (burst<6) {
	while (length<76){
	  digitalWrite(speakerOut,HIGH);
	  delayMicroseconds(Tone);
	  digitalWrite(speakerOut, LOW);
	  delayMicroseconds(Tone);
	  length=length+1;
	}
	delay (65);
	length = 0;
	burst = burst+1;
    }
    burst = 0; // reset for next pass 
}

// OneWire code adaptzed from http://www.arduino.cc/playground/Learning/OneWire
float readTemperature()
{
  byte i;
  byte present = 0;
  byte data[12];
  byte addr[8];
  
  int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;

  if ( !ds.search(addr)) {
      ds.reset_search();
  }
  
  if ( OneWire::crc8( addr, 7) != addr[7]) {
      Serial.print("CRC is not valid!\n");
      return 70.0f;
  }

  if ( addr[0] != 0x28) {
      Serial.print("Device is not a DS18S20 family device.\n");
      return 70.0f;
  } 

  ds.reset();
  ds.select(addr);
  ds.write(0x44,1);         // start conversion, with parasite power on at the end

  delay(1000);

  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad

  // we need 9 bytes
  for ( i = 0; i < 9; i++) {
    data[i] = ds.read();
  }
  
  //the shorter code for temp conversion
  int rawtemp = (data[1] << 8) + data[0];
  double tempc, tempf;
  tempc = (double)rawtemp / 16.0;
  tempf = (tempc * 1.8) + 32.0;
  
  return (float)tempf;
}

bool checkRearMotion()
{
  bool motionDetected = digitalRead(PIR_SENSOR_1_PIN);
  if (motionDetected)
  {
    digitalWrite(LED_1, HIGH);
    //Serial.write("Rear motion detected\n");
  }
  else
  {
    digitalWrite(LED_1, LOW);
    //Serial.write("No rear motion\n");
  }
  
  return motionDetected;
}

bool checkFrontMotion()
{
  bool motionDetected = digitalRead(PIR_SENSOR_2_PIN);
  if (motionDetected)
  {
    digitalWrite(LED_2, HIGH);
    //Serial.write("Front motion detected\n");
  }
  else
  {
    digitalWrite(LED_2, LOW);
    //Serial.write("No front motion\n");
  } 
  
  return motionDetected;
}

bool checkDoorClosed()
{
  bool doorClosed = digitalRead(DOOR_BUTTON);
  if (doorClosed)
    digitalWrite(LED_DOOR, HIGH);
  else
    digitalWrite(LED_DOOR, LOW);
  
  return doorClosed;
}

bool checkFrontPressure()
{
  bool pressure = false;
  pressure = digitalRead(FRONT_PRESSURE_SWITCH);
  
  if (pressure)
    digitalWrite(LED_PRESSURE_FRONT, HIGH);
  else
    digitalWrite(LED_PRESSURE_FRONT, LOW);
  
  return pressure;
}

bool checkRearPressure()
{
  bool pressure = false;
  pressure = digitalRead(REAR_PRESSURE_SWITCH);
  
  if (pressure)
    digitalWrite(LED_PRESSURE_REAR, HIGH);
  else
    digitalWrite(LED_PRESSURE_REAR, LOW);
  
  return pressure;
}
